package com.gitee.wsl.doc.prosemirror.history.ext

import com.gitee.wsl.doc.prosemirror.history.HistoryOptions
import com.gitee.wsl.doc.prosemirror.history.bean.HistoryPluginData
import com.gitee.wsl.doc.prosemirror.history.bean.HistoryState
import com.gitee.wsl.doc.prosemirror.history.closeHistoryKey
import com.gitee.wsl.doc.prosemirror.history.historyKey
import com.gitee.wsl.doc.prosemirror.history.mapRanges
import com.gitee.wsl.doc.prosemirror.history.rangesFor
import com.gitee.wsl.doc.prosemirror.state.EditorState
import com.gitee.wsl.doc.prosemirror.state.Transaction
import com.gitee.wsl.doc.prosemirror.transform.Transform


/** Record a transformation in undo history. */
@Suppress("ReturnCount", "ComplexMethod", "LongMethod")
fun HistoryState.applyTransaction(
    state: EditorState,
    tr: Transaction,
    options: HistoryOptions
): HistoryState {
    var thisHistory = this
    val historyTr = tr.getMeta(historyKey) as? HistoryPluginData
    var rebased: Int? = null
    if (historyTr != null) {
        return historyTr.historyState
    }

    if (tr.getMeta(closeHistoryKey) != null) {
        thisHistory = HistoryState(thisHistory.done, thisHistory.undone, null, 0, -1)
    }

    val appended = tr.getMeta("appendedTransaction") as? Transaction

    if (tr.steps.isEmpty()) {
        return thisHistory
    } else if (appended?.getMeta(historyKey) != null) {
        val appendedHistoryData = appended.getMeta(historyKey) as HistoryPluginData
        if (appendedHistoryData.redo) {
            return thisHistory.redo(state,tr,options)
//            return HistoryState(
//                thisHistory.done.addTransform(tr, null, options, mustPreserveItems(state)),
//                thisHistory.undone,
//                rangesFor(tr.mapping.maps),
//                thisHistory.prevTime,
//                thisHistory.prevComposition
//            )
        } else {
            return thisHistory.undone(state,tr,options)
//            return HistoryState(
//                thisHistory.done,
//                thisHistory.undone.addTransform(tr, null, options, mustPreserveItems(state)),
//                null,
//                thisHistory.prevTime,
//                thisHistory.prevComposition
//            )
        }
    } else if (tr.getMeta("addToHistory") != false && appended?.getMeta("addToHistory") != false) {
        // Group transforms that occur in quick succession into one event.
        val composition = tr.getMeta("composition") as? Int
        val newGroup = thisHistory.prevTime == 0L ||
                (appended == null && thisHistory.prevComposition != composition &&
                                (thisHistory.prevTime < tr.time - options.newGroupDelay ||
                                                !isAdjacentTo(tr, thisHistory.prevRanges!!)
                                 )
                )
        val prevRanges = if (appended != null) {
            mapRanges(thisHistory.prevRanges, tr.mapping)
        } else {
            rangesFor(tr.mapping.maps)
        }
        return thisHistory.done(state,tr,options,prevRanges,composition,newGroup)
//        val done = thisHistory.done.addTransform(
//            tr,
//            if (newGroup) state.selection.getBookmark() else null,
//            options,
//            mustPreserveItems(state)
//        )
//        return HistoryState(
//            done,
//            Branch.empty,
//            prevRanges,
//            tr.time,
//            composition ?: thisHistory.prevComposition
//        )
    } else if (tr.getMeta("rebased")?.also { rebased = it as Int } != null) {
        // Used by the collab module to tell the history that some of its
        // content has been rebased.
        return thisHistory.rebased(tr,rebased!!)
//        return HistoryState(
//            thisHistory.done.rebased(tr, rebased!!),
//            thisHistory.undone.rebased(tr, rebased!!),
//            mapRanges(thisHistory.prevRanges, tr.mapping),
//            thisHistory.prevTime,
//            thisHistory.prevComposition
//        )
    } else {
        return thisHistory.add(tr)
//        return HistoryState(
//            thisHistory.done.addMaps(tr.mapping.maps),
//            thisHistory.undone.addMaps(tr.mapping.maps),
//            mapRanges(thisHistory.prevRanges, tr.mapping),
//            thisHistory.prevTime,
//            thisHistory.prevComposition
//        )
    }
}

@Suppress("ReturnCount")
fun isAdjacentTo(transform: Transform, prevRanges: List<IntRange>): Boolean {
    if (prevRanges.isEmpty()) return false
    if (!transform.docChanged) return true
    var adjacent = false
    transform.mapping.maps[0].forEach { start, end, _, _ ->
        prevRanges.forEach { range ->
            if (start <= range.last && end >= range.first) {
                adjacent = true
            }
        }
    }
    return adjacent
}


